package eu.webhib.webblog.gui.controller;

import eu.webhib.frmwrk.jsf.base.EditBaseController;
import eu.webhib.frmwrk.jsf.utils.JsfUtil;
import eu.webhib.frmwrk.persistence.PersistenceMngr;
import eu.webhib.webblog.application.ApplMail;
import eu.webhib.webblog.domain.User;
import eu.webhib.webblog.service.factory.ServiceFactory;
import eu.webhib.webblog.gui.application.GuiApplication;
import eu.webhib.webblog.service.intf.UserService;
import java.util.regex.*;
import javax.faces.bean.*;
import javax.faces.event.*;
import org.apache.log4j.Logger;

/**
 * Controller of the User Registration page
 */
@ManagedBean(name = "reg")
@SessionScoped
public class Register extends EditBaseController {

   private static final Logger log = Logger.getLogger(Register.class);
   private static UserService userService = ServiceFactory.getUserService();
   // User input data
   private String displayName;
   private String email;
   private String userName;
   private String passWord;
   private String passWordConfirm;
   private boolean registered = false;

   //
   // User input  ----------------------------------------------------------
   //
   public String getDisplayName() {
      return displayName;
   }

   public void setDisplayName(String displayName) {
      this.displayName = displayName;
   }

   public String getEmail() {
      return email;
   }

   public void setEmail(String email) {
      this.email = email;
   }

   public String getUserName() {
      return userName;
   }

   public void setUserName(String userName) {
      this.userName = userName;
   }

   public String getPassWord() {
      return null;
   }

   public void setPassWord(String passWord) {
      this.passWord = passWord;
   }

   public String getPassWordConfirm() {
      return null;
   }

   public void setPassWordConfirm(String passWordConfirm) {
      this.passWordConfirm = passWordConfirm;
   }

   public boolean isRegistered() {
      return registered;
   }

   //
   // Registration actrion ---------------------------------------------------------------
   //
   public void registerListener(ComponentSystemEvent event) {
      log.info("registerListener");
      if (GuiApplication.isAuthenticated()) {
         JsfUtil.sendRedirect(GuiApplication.getNotAtLoggedInStatePageURI());
         return;
      }
      if (JsfUtil.isGet() && !registered) {
         setInfoMessage("info_register_new");
      }
   }

   public String registerAction() {
      log.info("registerAction");
      return new EditActionClosure() {

         @Override
         protected void run() {
            registered = doRegisterAction();
         }
      }.start();
   }

   private boolean doRegisterAction() {
      // check input data fields: displayName, email, userName
      checkDisplayName(displayName);
      checkEmail(email);
      checkUserName(userName);
      // check and compare password and confirmation password
      checkPasswords(passWord, passWordConfirm);

      // if there was an input error
      if (!isInputOk()) {
         passWord = null;
         passWordConfirm = null;
         return false;
      }

      // add new user to database, having status registration pending
      User user = addRegisteredUser();
      passWord = null;
      passWordConfirm = null;
      if (user == null) {
         // if there was an error at database write operation
         setFatalMessage("fatal_database_write");
         return false;
      }

      // send registration confirmation email to given email address
      if (!ApplMail.sendUserMail(
              user.getEmail(),
              "info_register_mail",
              "info_register_subject",
              GuiApplication.registerConfirmPage,
              user)) {
         // if there was an error at mail transmission
         setFatalMessage("fatal_sendmail");
         PersistenceMngr.setRollbackTransaction();
         return false;
      }

      // all ok
      setInfoMessage("info_register_ok", email);
      displayName = null;
      email = null;
      return true;
   }

   // helper functions for register action, also used by others (e.g. password reset, profile)
   public static void checkDisplayName(String displayName) {
      if (displayName.length() < 4 || displayName.length() > 30) {
         setFieldErrorFieldName("displayname", "error_field_length_range", 4, 30);
      } else if (displayNameExists(displayName)) {
         setFieldErrorFieldName("displayname", "error_field_user_exists");
      }
   }

   public static void checkEmail(String email) {
      if (!Pattern.matches("[^\\s@]+@[^\\s@]+?\\.[^\\s@]+", email)) {
         setFieldError("email", "error_email");
      }
   }

   public static void checkUserName(String userName) {
      if (userName.length() < 4 || userName.length() > 20) {
         setFieldErrorFieldName("username", "error_field_length_range", 4, 20);
      } else if (userNameExists(userName)) {
         setFieldErrorFieldName("username", "error_field_user_exists");
      }
   }

   // check passwords
   public static void checkPasswords(String passWord, String passWordConfirm) {
      if (passWord.length() < 5) {
         setFieldErrorFieldName("password", "error_field_length_minimum", 5);
      }
      if (!passWord.equals(passWordConfirm)) {
         setFieldError("passwordconfirm", "error_password_notmatching");
         passWordConfirm = "";
      }
   }

   private static boolean displayNameExists(String displayName) {
      return userService.findByAttribute("displayName", displayName).size() > 0;
   }

   private static boolean userNameExists(String userName) {
      return userService.findByAttribute("userName", userName).size() > 0;
   }

   private User addRegisteredUser() {
      log.info("addRegisteredUser");
      User user = null;
      try {
         user = userService.createNew();
         user.setRegistered(displayName, email, userName, passWord);
         userService.save(user);
      } catch (Throwable t) {
         log.error("addRegisteredUser", t);
         return null;
      }
      log.info("user added with userName: " + userName);
      return user;
   }

   // Registration successful
   public String registeredContinueAction() {
      JsfUtil.sendRedirect(GuiApplication.getHistroyPageURI());
      return null;
   }
}
